/**
 * Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.jfinal.plugin.activerecord.generator;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import com.jfinal.kit.JavaKeyword;
import com.jfinal.kit.StrKit;

/**
 * Base model 生成器
 */
public class BaseModelGenerator {
	
	protected String packageTemplate =
		"package %s;%n%n";
	protected String importTemplate =
		"import com.jfinal.plugin.activerecord.Model;%n" +
		"import com.jfinal.plugin.activerecord.IBean;%n%n";
	protected String classDefineTemplate =
		"/**%n" +
		" * Generated by JFinal, do not modify this file.%n" +
		" */%n" +
		"@SuppressWarnings(\"serial\")%n" +
		"public abstract class %s<M extends %s<M>> extends Model<M> implements IBean {%n%n";
	protected String classDefineForSetterChainTemplate =
			"/**%n" +
			" * Generated by JFinal, do not modify this file.%n" +
			" */%n" +
			"@SuppressWarnings({\"serial\", \"unchecked\"})%n" +
			"public abstract class %s<M extends %s<M>> extends Model<M> implements IBean {%n%n";
	protected String setterTemplate =
			"\tpublic void %s(%s %s) {%n" +
				"\t\tset(\"%s\", %s);%n" +
			"\t}%n%n";
	protected String setterChainTemplate =
			"\tpublic M %s(%s %s) {%n" +
				"\t\tset(\"%s\", %s);%n" +
				"\t\treturn (M)this;%n" +
			"\t}%n%n";
	
	protected String getterTemplate =
			"\tpublic %s %s() {%n" +
				"\t\treturn get(\"%s\");%n" +
			"\t}%n%n";
	
	protected String baseModelPackageName;
	protected String baseModelOutputDir;
	protected boolean generateChainSetter = false;
	
	protected JavaKeyword javaKeyword = JavaKeyword.me;
	
	public BaseModelGenerator(String baseModelPackageName, String baseModelOutputDir) {
		if (StrKit.isBlank(baseModelPackageName)) {
			throw new IllegalArgumentException("baseModelPackageName can not be blank.");
		}
		if (baseModelPackageName.contains("/") || baseModelPackageName.contains("\\")) {
			throw new IllegalArgumentException("baseModelPackageName error : " + baseModelPackageName);
		}
		if (StrKit.isBlank(baseModelOutputDir)) {
			throw new IllegalArgumentException("baseModelOutputDir can not be blank.");
		}
		
		this.baseModelPackageName = baseModelPackageName;
		this.baseModelOutputDir = baseModelOutputDir;
	}
	
	public void setGenerateChainSetter(boolean generateChainSetter) {
		this.generateChainSetter = generateChainSetter;
	}
	
	public void generate(List<TableMeta> tableMetas) {
		System.out.println("Generate base model ...");
		System.out.println("Base Model Output Dir: " + baseModelOutputDir);
		for (TableMeta tableMeta : tableMetas) {
			genBaseModelContent(tableMeta);
		}
		writeToFile(tableMetas);
	}
	
	protected void genBaseModelContent(TableMeta tableMeta) {
		StringBuilder ret = new StringBuilder();
		genPackage(ret);
		genImport(ret);
		genClassDefine(tableMeta, ret);
		for (ColumnMeta columnMeta : tableMeta.columnMetas) {
			genSetMethodName(columnMeta, ret);
			genGetMethodName(columnMeta, ret);
		}
		ret.append(String.format("}%n"));
		tableMeta.baseModelContent = ret.toString();
	}
	
	protected void genPackage(StringBuilder ret) {
		ret.append(String.format(packageTemplate, baseModelPackageName));
	}
	
	protected void genImport(StringBuilder ret) {
		ret.append(String.format(importTemplate));
	}
	
	protected void genClassDefine(TableMeta tableMeta, StringBuilder ret) {
		String template = generateChainSetter ? classDefineForSetterChainTemplate : classDefineTemplate;
		ret.append(String.format(template, tableMeta.baseModelName, tableMeta.baseModelName));
	}
	
	protected void genSetMethodName(ColumnMeta columnMeta, StringBuilder ret) {
		String setterMethodName = "set" + StrKit.firstCharToUpperCase(columnMeta.attrName);
		// 如果 setter 参数名为 java 语言关键字，则添加下划线前缀 "_"
		String argName = javaKeyword.contains(columnMeta.attrName) ? "_" + columnMeta.attrName : columnMeta.attrName;
		String template = generateChainSetter ? setterChainTemplate : setterTemplate;
		String setter = String.format(template, setterMethodName, columnMeta.javaType, argName, columnMeta.name, argName);
		ret.append(setter);
	}
	
	protected void genGetMethodName(ColumnMeta columnMeta, StringBuilder ret) {
		String getterMethodName = "get" + StrKit.firstCharToUpperCase(columnMeta.attrName);
		String getter = String.format(getterTemplate, columnMeta.javaType, getterMethodName, columnMeta.name);
		ret.append(getter);
	}
	
	protected void writeToFile(List<TableMeta> tableMetas) {
		try {
			for (TableMeta tableMeta : tableMetas) {
				writeToFile(tableMeta);
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * base model 覆盖写入
	 */
	protected void writeToFile(TableMeta tableMeta) throws IOException {
		File dir = new File(baseModelOutputDir);
		if (!dir.exists()) {
			dir.mkdirs();
		}
		
		String target = baseModelOutputDir + File.separator + tableMeta.baseModelName + ".java";
		FileWriter fw = new FileWriter(target);
		try {
			fw.write(tableMeta.baseModelContent);
		}
		finally {
			fw.close();
		}
	}
}






